//引入核心模块
const query = require('querystring')
const fs = require('fs')
const path = require('path')
const NodeRSA = require('node-rsa') //privatekey为应用私钥 
// 外部文件
const { getYMDHMS } = require('./src/utils/other')
//express服务器
const express = require('express')
const app = express()
const textData = 'text/plain;charset=utf8;' //文本型数据响应
// 生成支付链接
app.post('/create_pay_url', (req, res) => {
    console.log('请求路径：', url.parse(req.url, true).path)//请求完整路径
    // 接收数据
    accept_post(req, res).then(data => {
        let ctx = {} //接收传过来的data
        try {
            ctx = JSON.parse(data) //请求提交数据为json型
        } catch {
            ctx = query.parse(data) //请求数据为webform表单
        }
        console.log('支付回调对象：', ctx)
        // const obj = {
        //     order: 'xxx',//订单编号，必须保证唯一性，不可重复，建议使用时间戳
        //     money: 10,//订单金额，单位元
        //     goods: '半斤枸杞子',//商品名称
        //     payway: '微信',//支付方式，微信或者支付宝
        // }
        if (ctx.payway == '微信') return get_pay_url_wechat(ctx) //申请微信支付链接
        if (ctx.payway == '支付宝') return get_pay_url_alipay(ctx) //申请支付宝支付链接
    }).then(pay_url => {
        response(res, textData, 200, '请求成功', pay_url) //结束响应
    }).catch(err => {
        response(res, textData, 403, err.toString(), {})
    })

})
// 支付回调通知
app.post('/pay_notice', (req, res) => {
    console.log('请求路径：', url.parse(req.url, true).path)//请求完整路径
    let order_number, pay_order
    // 接收数据
    accept_post(req, res).then(data => {
        let ctx = {} //接收传过来的data
        try {
            ctx = JSON.parse(data) //请求提交数据为json型
        } catch {
            ctx = query.parse(data) //请求数据为webform表单
        }
        console.log('支付回调对象：', ctx)
        // 有id参数为微信支付
        if (ctx.id && ctx.summary == '支付成功') {
            // 微信回调信息解密
            const { Aes: { AesGcm } } = require('wechatpay-axios-plugin')
            const secret = 'xxxx' //apiv3密钥
            const ciphertext = ctx.resource.ciphertext //密文
            const nonce = ctx.resource.nonce //随机串
            const associated_data = ctx.resource.associated_data //附带信息
            order_number = JSON.parse(AesGcm.decrypt(nonce, secret, ciphertext, associated_data)).out_trade_no //订单编号
            pay_order = JSON.parse(AesGcm.decrypt(nonce, secret, ciphertext, associated_data)).transaction_id //支付单号
        }
        // 否则为支付宝通知
        else if (ctx.trade_status == 'TRADE_SUCCESS') {
            order_number = ctx.out_trade_no  //订单编号
            pay_order = ctx.trade_no //支付单号
        }
        else throw '不明平台支付订单'

        console.log('订单编号：', order_number)
        console.log('支付单号：', pay_order)

        if (!order_number) throw '未拿到订单编号'
        if (!pay_order) throw '未拿到支付单号'
        response(res, textData, 200, '请求成功', { order_number, pay_order }) //结束响应
    }).catch(err => {
        response(res, textData, 403, err.toString(), {})
    })


})
// 批准OPTIONS请求解决跨域 
app.options('*', (req, res) => {
    response(res, textData, 200, '批准OPTIONS请求', {})
})
// 启动监听
app.listen(9000, () => {
    console.log('启动成功！')
})
/**
 *   设置数据格式和编码以及结束响应
 * @param {object} res  express中res
 * @param {string} dataType  数据类型
 * @param {number} status  状态码
 * @param {string} msg  信息说明
 * @param {object} data  数据对象
 * @return {void} 无返回值
**/
//设置数据格式和编码以及结束响应
function response(res, dataType, status, msg, data) {
    let obj = {}
    obj.code = status //状态码代码
    obj.msg = msg //提示信息
    obj.data = data //返回数据
    res.writeHead(200, {
        "Content-Type": dataType, //数据类型
        "Access-Control-Allow-Origin": "*", //解决跨域关键
        "transfer-encoding": "chunked",
        "Access-Control-Allow-Headers": "*"
    })
    console.log('响应数据：', obj)
    res.end(JSON.stringify(obj)) //结束响应
}
/**
 *   接收post请求数据
 * @param {object} req  express中req
 * @param {object} res  express中res
 * @return {Promise<string>} 返回post数据拼接完后的string字符串
**/
// 接收post请求数据
function accept_post(req, res) {
    return new Promise((resovle, reject) => {
        let data = '' //储存数据变量
        let place = 0 //定义结束方法位置

        req.on('data', chunk => {
            data += chunk.toString() //拼接POST数据
        })
        //POST数据接收完毕1
        req.on('close', () => {
            if (place == 0) {
                place = 1
                resovle(data)
            }
        })
        //POST数据接收完毕2
        req.on('end', () => {
            if (place == 0) {
                place = 1
                resovle(data)
            }
        })
        //POST数据接收出错
        req.on('error', (e) => {
            reject('数据接收出错')
        })
    })

}
/**
 *   RSA2-sha256签名
 * @param {string} privatekey  私钥
 * @param {string} context  待签名文本
 * @return {string} 返回签名结果
**/
// RSA2-sha256签名
function rsa2_sign(privatekey, context) {
    const key = new NodeRSA();
    key.setOptions({ b: 1024, signingScheme: "sha256" });  //必须为sha256
    key.importKey(privatekey, 'pkcs8-private');//导入密钥并设定格式
    const publicDer = key.exportKey('pkcs8-public');
    // console.log(publicDer); //打印出应用公钥
    let buffer = Buffer.from(context);//context为需要签名的内容如：app_id=101245122&name=小米&age=666
    let signature = key.sign(buffer).toString('base64');
    return signature //返回签名结果
}
/**
 *   支付宝申请支付链接
 * @param {object} order_info - 订单的信息对象
 * @return {string} 返回申请到的支付链接url
**/
// 支付宝申请支付链接
function get_pay_url_alipay(order_info) {
    const pay_time_out = 10 //支付超时控制，单位分钟
    const appid = 'xxxxx' //支付宝APPID
    // 支付宝私钥
    const privatekey = 'xxx'
    const notify_url = 'https://service-4n5v3qzo-1316704400.hk.apigw.tencentcs.com/release/pay_notice' //支付宝支付通知回调地址
    const return_url = 'https://chatgpt.indexls.com' //支付成功后跳转的url
    // 支付订单信息
    const order = {
        out_trade_no: order_info.order,//商品订单号
        total_amount: order_info.money, //商品价格
        subject: order_info.goods, //商品名称
        product_code: 'FAST_INSTANT_TRADE_PAY', //PC必传
        time_expire: getYMDHMS(Date.now() + 1000 * 60 * pay_time_out), //使用北京时间
    }
    // 请求动作列表
    const methodList = {
        h5: 'alipay.trade.wap.pay', //手机网站
        PC: 'alipay.trade.page.pay',//电脑网站
    }
    //请求参数
    const params = {
        //顺序至关重要
        app_id: appid,
        biz_content: JSON.stringify(order),
        charset: 'utf-8',
        method: methodList['h5'],
        notify_url: notify_url, //添加路径
        return_url: return_url,//支付成功后跳转的url
        sign_type: 'RSA2',
        timestamp: getYMDHMS(), //当前时间戳
        version: '1.0',
    }
    params.sign = rsa2_sign(privatekey, query.unescape(query.stringify(params)))  //数据签名
    // console.log('签名结果', params.sign)
    // 手机网站或电脑网站
    let pay_url = 'https://openapi.alipay.com' + '/gateway.do?' + query.stringify(params)
    return pay_url //返回支付链接
    // console.log('支付链接：', pay_url)
}
/**
 *   微信申请支付链接
 * @param {object} order_info - 订单的信息对象
 * @return {Promise<string>} 返回申请到的支付链接url
**/
// 微信申请支付链接
function get_pay_url_wechat(order_info) {
    // console.log(order_info)
    const { Wechatpay } = require('wechatpay-axios-plugin')// 微信支付sdk接口
    const mchid = 'xxxx'// 商户号，支持「普通商户/特约商户」或「服务商商户」
    const appid = 'xxx' //微信APPID
    const merchantCertificateSerial = 'xxx'// 「商户API证书」的「证书序列号」
    const notify_url = 'https://service-4n5v3qzo-1316704400.hk.apigw.tencentcs.com/release/pay_notice'
    const merchantPrivateKeyFilePath = path.join(__dirname, './src/wechat/apiclient_key.pem')// 从本地文件中加载「商户API私钥」
    const merchantPrivateKeyInstance = fs.readFileSync(merchantPrivateKeyFilePath)
    const platformCertificateSerial = '123456'// 「微信支付平台证书」的「证书序列号」，下载器下载后有提示`serial`序列号字段
    const platformCertificateFilePath = path.join(__dirname, './src/wechat/apiclient_cert.pem') // 从本地文件中加载「微信支付平台证书」，用来验证微信支付请求响应体的签名
    const platformCertificateInstance = fs.readFileSync(platformCertificateFilePath)
    // 初始化
    const wxpay = new Wechatpay({
        mchid,
        serial: merchantCertificateSerial,
        privateKey: merchantPrivateKeyInstance,
        certs: { [platformCertificateSerial]: platformCertificateInstance, },
    })
    // 发起申请
    return wxpay.v3.pay.transactions.native.post({
        mchid, //商户号
        out_trade_no: order_info.order, //商品订单编号
        appid, //微信APPID
        description: order_info.goods, //商品名称
        notify_url, //支付通知回调地址
        amount: {
            total: order_info.money * 100, //商品价格单位为分
            currency: 'CNY'
        },
    }).then(({ data: { code_url } }) => {
        //  笑死，此处毛也没有
    }).catch(({ response: { status, statusText, data } }) => data).then(data => {
        return JSON.parse(data).code_url  //返回支付链接
    })

}